#include "ListeTour.h"
#include <stdlib.h>
#include <stdio.h>
#include <cassert>

/* Cette fonction est un accesseur au pointeur sur tour :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure liste de tours ainsi que les parametres X et Y de la tour à trouver.
 * Elle renvoie un pointeur sur une structure tour ou NULL si elle na pas trouvé la tour prévue. */
Stour* get_listetour (const Slistetour* listtr, int Xatrouver, int Yatrouver)
{
	// Si le pointeur sur la liste de tours est null ou qu'il n'y a en tout cas pas de tour pointée, on ne peut acceder à rien
	if ((listtr==NULL) || ((*listtr).ptour==NULL))
	{
		return(NULL);
	}
	else
	{
		// sinon, on cherche la tour qui a bien les bonnes coordonnées :
		if ((((*(*listtr).ptour).X)==Xatrouver) && (((*(*listtr).ptour).Y)==Yatrouver))
		{
			// et on renvoi le pointeur qui lui est associé
			return ((*listtr).ptour);
		}
		else
		{
			// tant qu'on ne l'a pas trouvé, on rappelle récursivement sur le reste de la liste jusqu'à ce qu'on trouve ou
			// que la liste soit vide.
			return (get_listetour (((*listtr).ptoursuiv), Xatrouver, Yatrouver));
		}
	}
}

/* Cette procédure est un mutateur de la liste de tours.
 * ATTENTION, APRES SON EXECUTION, LE POINTEUR SUR LA LISTE DE TOURS SERA MODIFIE !
 * Elle prend en paramètres un pointeur sur la liste de tours ainsi qu'un pointeur sur une tour (qui ne pourra etre modifié !)
 * Elle sera utilisée afin d'upgrader une tour. */
void set_listetour (Slistetour* listtr, const Stour* tr)
{
	// Tout d'abord, on accede via un pointeur temporaire à la tour qui nous interesse
	Stour* ptouramodif;
	ptouramodif=get_listetour(listtr, (*tr).X, (*tr).Y);
	// puis on l'upgrade !
	upgrade(ptouramodif);
}

/* Procédure qui va permettre l'initialisation de la structure liste de tours.
 * Elle prend en paramètre un pointeur sur la structure liste de tour.
 * Cette procédure initialise les 2 pointeurs de la structure (le pointeur sur tour et le pointeur sur suivant) à NULL. */
void init_listetour (Slistetour* listtr)
{
	(*listtr).ptour=NULL;
	(*listtr).ptoursuiv=NULL;
}

/* Procédure qui va permettre d'ajouter une tour à la fin de la liste.
 * Elle prend en paramètres un pointeur sur une structure liste de tours, des coordonnées X et Y (qui ne pourront etre modifiées),
 * un numéro de type (qui ne pourra etre modifié) ainsi qu'un pointeur sur la liste de types (qui ne pourra etre modifié). */
void ajouttour (Slistetour* listtr, const int nvoX, const int nvoY, const int numtyp, Sterrain* ter, const Stype* ADRESSE_LISTE_TYPES)
{
	// avant tout, on vérifie qu'on essaye pas d'ajouter une tour à un emplacement non libre
	assert ((get_terrain (nvoX, nvoY, ter))==0);
	// si ce n'est pas le cas alors on vérifie qu'on est à la fin de la liste
	if ((*listtr).ptoursuiv==NULL)
	{
		// on créé et on initialise nos 2 pointeurs (sur une structure liste de tours et sur une structure tour)
		(*listtr).ptoursuiv=new Slistetour;
		init_listetour((*listtr).ptoursuiv);
		(*listtr).ptour = new Stour;
		init_tour((*listtr).ptour, ADRESSE_LISTE_TYPES);
		// ensuite on modifie la tour insérée selon les parametres puis on met le terrain à jour.
		set_tour((*listtr).ptour, numtyp, nvoX, nvoY);
		set_terrain (nvoX, nvoY, ter, ((numtyp)/10)+1);
	}
	else
	{
		// tant qu'on est pas à la fin de la liste, on rappelle la fonction sur l'element suivant.
		ajouttour ((*listtr).ptoursuiv, nvoX, nvoY, numtyp, ter, ADRESSE_LISTE_TYPES);
	}
}

/* Procédure qui va permettre de supprimer une tour de la liste.
 * Elle prend en parametres un pointeur sur la liste de tours, des coordonnées X et Y qui ne pourront etre modifiées et
 * un pointeur sur la structure terrain. Attention, après execution, le terrain et la liste de tours seront modifiés. */
void suptour (Slistetour* listtr, const int X, const int Y, Sterrain* ter)
{
	// on vérifie qu'on essaye pas d'enlever une tour inexistante
	assert ((get_terrain (X, Y, ter))>1);	
	// on créé des pointeurs temporaires qui vont nous permettre de conserver et comparer parent, enfant et courant.
	Stour* ptourasup;
	Slistetour* ptempparent;
	Slistetour* ptempcompte;
	Slistetour* ptempsuivant;
	// on met dans tourasup la tour à supprimer
	ptourasup=get_listetour (listtr, X, Y);
	// et dans ptempcompte le pointeur courant
	ptempcompte=listtr;
	// si on est sur l'element à supprimer, alors on supprime et c'est terminé
	if ((*ptempcompte).ptour==ptourasup)
	{
		listtr=(*ptempcompte).ptoursuiv;
		delete ptempcompte;
	}
	else
	{
		// sinon, tant qu'on a pas trouvé la tour a supprimer, on met le pointeur courant dans ptempparent puis on incrémente
		// le pointeur courant.
		while ((*ptempcompte).ptour!=ptourasup)
		{
			ptempparent=ptempcompte;
			ptempcompte=(*ptempcompte).ptoursuiv;
		}
		// une fois qu'on l'a trouvé, on copie le fils dans ptempsuivant
		ptempsuivant=(*ptempcompte).ptoursuiv;
		// on efface le pointeur sur tour puis le pointeur sur lélément complet qui était a supprimer
		delete (*ptempcompte).ptour;
		delete ptempcompte;
		// puis on recopie dans le parent le ptempsuivant qui représente le fils qu'on ne voulait pas perdre.
		(*ptempparent).ptoursuiv=ptempsuivant;
		// enfin, on met à jour le terrain
		set_terrain (X, Y, ter, 0);
	}
}

/* Procédure qui va supprimer l'ensemble des tours de la liste.
 * Elle prend en parametres un pointeur sur la liste de tours. */
void vider_listetour (Slistetour* listtr)
{
	// si on n'est pas à la fin de la liste, on rappelle la fonction sur l'élément suivant
	if ((*listtr).ptoursuiv!=NULL)
	{
		vider_listetour ((*listtr).ptoursuiv);
	}
	// une fois qu'on y est, on fait la suppression d'abord de la tour puis de la liste de tour correspondant.
	delete ((*listtr).ptour);
	(*listtr).ptour=NULL;
	delete (listtr);
	listtr=NULL;
}
